#include <Arduino.h>
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <ArduinoJson.h>
#include "freertos/queue.h"
extern "C"
{
#include "esp_wifi.h"
}

#define MAX_DEVICES 10
#define EVENT_QUEUE_SIZE 50

struct TrackedDevice
{
  String mac;
  bool active;
  uint32_t lastTriggerTime; // 上次触发的时间戳（毫秒）
  int rssi;                 // 新增：记录信号强度
};

struct DeviceEvent
{
  uint8_t mac[6];
  uint32_t timestamp;
};

QueueHandle_t eventQueue;
TaskHandle_t selfTestTaskHandle = NULL;
TrackedDevice trackedDevices[MAX_DEVICES];
AsyncWebServer server(80);

static int taskIndex[MAX_DEVICES]; // 用于传递任务索引，避免动态内存分配
const char *ssid = "CMCC-zpsu";
const char *password = "18965136219szp";
// LED GPIOs for each tracked device
int ledPins[MAX_DEVICES] = {2, 4, 5, 13, 25, 26, 27, 32, 33, 23};

void blinkLED(void *parameter)
{
  int index = *((int *)parameter);
  bool wasActive = false;

  for (;;)
  {
    if (trackedDevices[index].active)
    {
      wasActive = true;
      digitalWrite(ledPins[index], !digitalRead(ledPins[index]));
      vTaskDelay(pdMS_TO_TICKS(500));
    }
    else
    {
      digitalWrite(ledPins[index], LOW);

      // 如果曾经激活过，且现在连续 inactive，退出任务释放资源
      if (wasActive)
      {
        Serial.printf("Task for device %d exiting\n", index);
        vTaskDelete(NULL); // 自删任务
      }

      vTaskDelay(pdMS_TO_TICKS(5000)); // inactive 时延时更长
    }
  }
}

bool matchMAC(String mac1, const uint8_t *mac2)
{
  char buf[18];
  sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X",
          mac2[0], mac2[1], mac2[2], mac2[3], mac2[4], mac2[5]);
  return mac1.equalsIgnoreCase(buf);
}

void sniffer(void *buf, wifi_promiscuous_pkt_type_t type)
{
  if (type != WIFI_PKT_MGMT)
    return;
  wifi_promiscuous_pkt_t *pkt = (wifi_promiscuous_pkt_t *)buf;
  const uint8_t *mac = pkt->payload + 10;

  /****
   * // 调试输出
  char macStrDebug[18];
  sprintf(macStrDebug, "%02X:%02X:%02X:%02X:%02X:%02X",
          mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  Serial.printf("Sniffer: MAC=%s, RSSI=%d dBm\n", macStrDebug, pkt->rx_ctrl.rssi);
  ****/

  // 将 MAC 转为字符串
  char macStr[18];
  sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X",
          mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

  bool found = false;
  for (int i = 0; i < MAX_DEVICES; i++)
  {
    // 如果已存在该 MAC
    if (trackedDevices[i].mac.equalsIgnoreCase(macStr))
    {
      trackedDevices[i].rssi = pkt->rx_ctrl.rssi; // 更新 RSSI
      found = true;
      // 如果当前设备被跟踪，则亮灯
      if (trackedDevices[i].active)
      {
        digitalWrite(ledPins[i], HIGH);
      }
      break;
    }
  }

  // 如果没找到该 MAC，则存入第一个空槽
  if (!found)
  {
    for (int i = 0; i < MAX_DEVICES; i++)
    {
      if (trackedDevices[i].mac.length() == 0)
      {
        trackedDevices[i].mac = String(macStr);
        trackedDevices[i].rssi = pkt->rx_ctrl.rssi;
        trackedDevices[i].active = false; // 默认不跟踪
        break;
      }
    }
  }
}

void processEvents(void *parameter)
{
  DeviceEvent evt;
  for (;;)
  {
    if (xQueueReceive(eventQueue, &evt, portMAX_DELAY))
    {
      for (int i = 0; i < MAX_DEVICES; i++)
      {
        if (trackedDevices[i].active && matchMAC(trackedDevices[i].mac, evt.mac))
        {
          uint32_t now = millis();
          if (now - trackedDevices[i].lastTriggerTime > 3000)
          { // 防抖间隔 3 秒
            trackedDevices[i].lastTriggerTime = now;
            digitalWrite(ledPins[i], HIGH);
          }
          break;
        }
      }
    }
  }
}

// ========= 【自检任务】=========
void selfTestTask(void *pv)
{
  Serial.println("Self-test: start");

  /* 0. 挂起所有 blinkLED 任务，避免冲突 */
  for (int i = 0; i < MAX_DEVICES; i++) {
    char taskName[16];
    sprintf(taskName, "blinkLED_%d", i);
    TaskHandle_t h = xTaskGetHandle(taskName);
    if (h != NULL) vTaskSuspend(h);
  }

  /* 1. 依次点亮 */
  for (int i = 0; i < MAX_DEVICES; i++) {
    digitalWrite(ledPins[i], HIGH);
    Serial.printf("Self-test: LED on GPIO %d HIGH\n", ledPins[i]);
    vTaskDelay(pdMS_TO_TICKS(200));
  }

  vTaskDelay(pdMS_TO_TICKS(5000));

  /* 2. 全部熄灭 */
  for (int i = 0; i < MAX_DEVICES; i++) {
    digitalWrite(ledPins[i], LOW);
  }

  /* 3. 恢复 active 通道的高电平 */
  for (int i = 0; i < MAX_DEVICES; i++) {
    if (trackedDevices[i].active && trackedDevices[i].mac.length() > 0) {
      digitalWrite(ledPins[i], HIGH);
    } else {
      digitalWrite(ledPins[i], LOW);
    }
  }

  /* 4. 恢复所有 blinkLED 任务 */
  for (int i = 0; i < MAX_DEVICES; i++) {
    char taskName[16];
    sprintf(taskName, "blinkLED_%d", i);
    TaskHandle_t h = xTaskGetHandle(taskName);
    if (h != NULL) vTaskResume(h);
  }

  Serial.println("Self-test: done");
  selfTestTaskHandle = NULL;
  vTaskDelete(NULL);
}

void setup()
{
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  delay(100);
  Serial.println("Enabling promiscuous mode...");
  esp_wifi_set_promiscuous(true);
  esp_wifi_set_promiscuous_rx_cb(&sniffer);

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println(WiFi.localIP());

  for (int i = 0; i < MAX_DEVICES; i++)
  {
    trackedDevices[i].active = false;
    trackedDevices[i].rssi = 0; // 新增
    trackedDevices[i].lastTriggerTime = 0;
    pinMode(ledPins[i], OUTPUT);
    digitalWrite(ledPins[i], LOW);
    trackedDevices[i].active = false;
    char taskName[16];
    sprintf(taskName, "blinkLED_%d", i);
    static int taskIndex[MAX_DEVICES]; // 放在 setup 外部，或作为 setup 静态变量
    taskIndex[i] = i;
    xTaskCreate(blinkLED, taskName, 2048, &taskIndex[i], 1, NULL);
  }

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    String html = F("<!DOCTYPE html><html><head><meta charset='UTF-8'>"
                    "<meta name='viewport' content='width=device-width, initial-scale=1'>"
                    "<style>"
                    "body{font-family:Arial, sans-serif; margin:10px;}"
                    "table{border-collapse:collapse; width:100%; margin-bottom:20px;}"
                    "th, td{border:1px solid #ccc; padding:8px; text-align:center;}"
                    "th{background:#f2f2f2;}"
                    "tr:nth-child(even){background:#fafafa;}"
                    "a{color:#007BFF; text-decoration:none;}"
                    "a:hover{text-decoration:underline;}"
                    "</style></head><body>"
                    "<h2>📡 Tracked Devices</h2>"
                    "<p><a href='/'>🔄 Refresh </a> <a href='/selftest'>🔧 Check LED</a></p>");

    /* 1️⃣ 已绑定表格 */
    html += F("<h3>已绑定 (Active)</h3>");
    bool hasActive = false;
    html += F("<table><tr><th>#</th><th>LED</th><th>MAC</th><th>RSSI (dBm)</th><th>操作</th></tr>");
    for (int i = 0, idx = 1; i < MAX_DEVICES; i++) {
      if (trackedDevices[i].active && trackedDevices[i].mac.length()) {
        hasActive = true;
        html += "<tr>"
                "<td>" + String(idx++) + "</td>"
                "<td>" + String(i + 1, DEC) + "</td>"        // ← 新增 LED 编号
                "<td>" + trackedDevices[i].mac + "</td>"
                "<td>" + String(trackedDevices[i].rssi) + "</td>"
                "<td><a href='/toggle?i=" + String(i) + "'>解绑</a></td>"
                "</tr>";
      }
    }
    if (!hasActive) html += F("<tr><td colspan='5'>暂无</td></tr>");
    html += F("</table>");

    /* 2️⃣ 待绑定列表（所有扫描到且未绑定），按 RSSI 降序 */
    html += F("<h3>待绑定 (Inactive)</h3>");

    /* 收集所有未绑定的 MAC 及其 RSSI */
    struct Entry {
      String mac;
      int    rssi;
    };
    std::vector<Entry> inactive;
    for (int i = 0; i < MAX_DEVICES; i++) {
      if (!trackedDevices[i].active &&
          trackedDevices[i].mac.length() &&
          trackedDevices[i].rssi != 0) {
        inactive.push_back({trackedDevices[i].mac, trackedDevices[i].rssi});
      }
    }

    html += F("<table><tr><th>#</th><th>MAC</th><th>RSSI (dBm)</th><th>操作</th></tr>");
    if (inactive.empty()) {
      html += F("<tr><td colspan='5'>暂无</td></tr>");
    } else {
      for (size_t k = 0; k < inactive.size(); ++k) {
        html += "<tr>"
                "<td>" + String(k + 1) + "</td>"
                "<td>" + inactive[k].mac + "</td>"
                "<td>" + String(trackedDevices[k].rssi) + "</td>"
                "<td><a href='/toggle?i=" + String(k) + "'>绑定</a></td>"
                "</tr>";
      }
    }
    html += F("</table>");

    request->send(200, "text/html; charset=UTF-8", html);
  });

  server.on("/toggle", HTTP_GET, [](AsyncWebServerRequest* request) {
        if (request->hasParam("i")) {
          int i = request->getParam("i")->value().toInt();
          if (i >= 0 && i < MAX_DEVICES) {
            // 切换状态
            trackedDevices[i].active = !trackedDevices[i].active;

            // 如果这个位置还没有 MAC，尝试绑定最新扫描到的设备
            if (trackedDevices[i].mac.length() == 0) {
              for (int j = 0; j < MAX_DEVICES; j++) {
                if (trackedDevices[j].mac.length() > 0 && !trackedDevices[j].active) {
                  trackedDevices[i].mac = trackedDevices[j].mac;
                  trackedDevices[i].rssi = trackedDevices[j].rssi;
                  break;
                }
              }
            }
          }
        }
        request->redirect("/");
    });

  // ========= 【自检路由】=========
  server.on("/selftest", HTTP_GET, [](AsyncWebServerRequest* request) {
    // 如果自检任务已在运行，直接忽略
    if (selfTestTaskHandle != NULL) {
      request->redirect("/");
      return;
    }
    BaseType_t ok = xTaskCreate(
        selfTestTask,
        "selfTest",
        2048,
        NULL,
        1,
        &selfTestTaskHandle);
    if (ok != pdPASS) {
      Serial.println("Failed to create self-test task");
    }
    request->redirect("/");   // 立即返回
  });
  // ========= 【结束】=========

  eventQueue = xQueueCreate(EVENT_QUEUE_SIZE, sizeof(DeviceEvent));
  if (eventQueue == NULL)
  {
    Serial.println("Failed to create event queue");
  }
  else
  {
    xTaskCreate(processEvents, "processEvents", 4096, NULL, 1, NULL);
  }

  server.begin();
  WiFi.onEvent([](WiFiEvent_t event, WiFiEventInfo_t info)
               {
    Serial.println("WiFi ready, enabling promiscuous mode");
    esp_wifi_set_promiscuous(true);
    esp_wifi_set_promiscuous_rx_cb(&sniffer); }, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP);
}

void loop()
{
  delay(1000);
}